﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace theRift
{
    public class Player : Entity
    {
        protected int curHealth;
        protected int maxHealth;
        protected MouseState oldMouseState;

        public int CurHealth
        {
            get { return curHealth; }
            set { curHealth = value; }
        }

        public int MaxHealth
        {
            get { return maxHealth; }
            set { maxHealth = value; }
        }

        public Player(Game game)
            : base(game)
        {
        }

        public void UpdatePosition(GameTime gameTime)
        {
            KeyboardState keyboardState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // Determine rotation amount from input
            Vector2 rotationAmount = Vector2.Zero;
            if (keyboardState.IsKeyDown(Keys.Q))
                rotationAmount.X = 1.0f;
            if (keyboardState.IsKeyDown(Keys.E))
                rotationAmount.X = -1.0f;

            if (mouseState.RightButton == ButtonState.Pressed)
            {
                rotationAmount.X -= (MathHelper.Pi / 180) * (mouseState.X - oldMouseState.X);
            }

            // Determine thrust amount from input
            float thrustAmount = 0;
            if (keyboardState.IsKeyDown(Keys.W))
                thrustAmount = 1.0f;
            if (keyboardState.IsKeyDown(Keys.S))
                thrustAmount = -0.4f;

            // Determine strafe amount from input
            float strafeAmount = 0;
            if (keyboardState.IsKeyDown(Keys.A))
                strafeAmount = -1.0f;
            if (keyboardState.IsKeyDown(Keys.D))
                strafeAmount = 1.0f;

            oldMouseState = mouseState;

            // Scale rotation amount to radians per second
            rotationAmount = rotationAmount * 1.5f * elapsed; // 1.5f rotation sensitivity

            // Correct the X axis steering when the ship is upside down
            if (physicalState.Up.Y < 0)
                rotationAmount.X = -rotationAmount.X;

            // Create rotation matrix from rotation amount
            Matrix rotationMatrix =
                Matrix.CreateFromAxisAngle(physicalState.Right, rotationAmount.Y) *
                Matrix.CreateRotationY(rotationAmount.X);

            // Rotate orientation vectors
            physicalState.Direction = Vector3.TransformNormal(physicalState.Direction, rotationMatrix);
            physicalState.Up = Vector3.TransformNormal(physicalState.Up, rotationMatrix);

            // Re-normalize orientation vectors
            // Without this, the matrix transformations may introduce small rounding
            // errors which add up over time and could destabilize the ship.
            physicalState.Direction.Normalize();
            physicalState.Up.Normalize();

            // Re-calculate Right
            physicalState.Right = Vector3.Cross(physicalState.Direction, physicalState.Up);

            // The same instability may cause the 3 orientation vectors may
            // also diverge. Either the Up or Direction vector needs to be
            // re-computed with a cross product to ensure orthagonality
            physicalState.Up = Vector3.Cross(physicalState.Right, physicalState.Direction);

            // Calculate force from thrust amount
            Vector3 force = physicalState.Direction * thrustAmount *physicalState.ThrustForce;
            Vector3 strafe = physicalState.Right * strafeAmount *physicalState.ThrustForce;

            // Apply acceleration
            Vector3 acceleration = (force + strafe) / physicalState.Mass;
            physicalState.Velocity += acceleration * elapsed;

            // Apply psuedo drag
            physicalState.Velocity *= physicalState.DragFactor;

            // Apply velocity
            physicalState.Position += physicalState.Velocity * elapsed;

            // Reconstruct the ship's world matrix
            world = Matrix.Identity;
            world.Forward = physicalState.Direction;
            world.Up = physicalState.Up;
            world.Right = physicalState.Right;
            world.Translation = physicalState.Position;
        }

        public override void Update(GameTime gameTime)
        {
            UpdatePosition(gameTime);
            
            base.Update(gameTime);
        }
    }
}
